#!/usr/bin/env python
# -*- coding: utf-8 -*-

from define import DEFSTOCK
from define import INIT_AMOUNT
from define import TRADER_LOW
from define import TRADER_HIGH
from define import TRADE_STATE_S
from define import TRADE_STATE_W
from define import TRADE_STATE_F
from define import TRADE_STATE_A
from define import HOST
from define import PORT
from define import MAX_CLIENT

from dbop import CREATE_DB_STATUS_SCRIPT 
from dbop import CRTEAT_TRADER_INFO_TABLE 
from dbop import CRTEAT_TRADER_REQUEST_TABLE_SCRIPT 
from dbop import INSERT_DEFAULT_TARDERS_SCRIPT 

from stock import Stock
from stock import StockPool
from trade import TradeRequest

import multiprocessing.synchronize
from multiprocessing import Process
import os
import socket
import time
import logging
from threading import Thread
from threading import Event
import random
import unittest
import errno
import sys
import sqlite3
import datetime
import signal

def onexit(f):
    import atexit
    atexit.register(f)
    return f

class TraderManager(object):
    ''' StockExchange should run a client manager to store stock owning
    information of each trader client.

    Trade process:
    1. Trader get online through security check;
    2. Trader post request to client pool;
    3. Trader pool post request to stock exchange as a proxy;
    4. Stock exchange then pass the request to each stok;
    '''
    class ServerThread(Thread):
        def __init__(self, tm=None):
            Thread.__init__(self)
            self.name = 'Server Thread'
            self.stopped = True
            self.workers = []
            self.trader_manager = tm

            self.log = logging.getLogger("StockExchange")
            self.log.warning("CLIENTMANAGER: %s" % ("Server thread created."))

            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

            #signal.signal(signal.SIGALRM, self.kill)

        def __str__(self):
            return self.name

        def run(self):
            try:
                self.sock.bind((HOST, PORT))
                self.sock.listen(MAX_CLIENT)
                self.log.warning("TRADERMANAGER: %s: We're listening on port %d" 
                                 % (self, PORT))
            except socket.error, e:
                self.log.warning("TRADERMANAGER: %s: Socket error: %s" % (self, PORT))
                return

            self.stopped = False
            while not self.stopped:
                try:
                    # ISSUE: How to terminate this thread stuck in accept()???
                    # Stuck here when main threan exit, 
                    # Set as daemon to avoid this issue !!!
                    conn, addr = self.sock.accept()
                except socket.error, e:
                    continue
                except:
                    break
                self.log.warning("TRADERMANAGER: %s: Connected by %s"
                                 % (self, addr[0]))

                w = TraderManager.RequestHandlerThread(conn, addr,
                                                       self.trader_manager)
                w.dameon = True
                w.start()
                self.workers.append(w)

            # If we're here, it mean we should exit, so clean up.
            self.log.warning("TRADERMANAGER: %s: We're cleaning" % self)
            self.server_thread.stop()
            try:
                self.sock.shutdown(socket.SHUT_RDWR)
                self.sock.close()
            except:     # Force a shutdown ignoring any exceptions
                pass

            for w in self.workers:
                if w.isActive():
                    w.stop()
                #w.join()

        def stop(self):
            self.log.warning("TRADERMANAGER: %s: We're stopping!" % self)
            self.stopped = True

        def kill(self):
            del self.sock
            pass

    class RequestHandlerThread(Thread):
        def __init__(self, conn=None, addr=None, tm=None):
            Thread.__init__(self)
            self.name = 'RequestHandlerThread'
            self.stopped = True

            self.conn = conn
            self.addr = addr

            self.trader_manager = tm

            self.log = logging.getLogger("TraderManager")
            #self.log.warning("TRADERMANAGER: %s: We're created." % self)

        def __str__(self):
            return self.name

        def run(self):
            self.stopped = False
            try:
                data = self.conn.recv(100)
            except socket.error, e:
                self.log.critical("TRADERMANAGER: %s: Socket recv Error: %s"
                                  % (self, e))
            if not data:
                self.log.warning("TRADERMANAGER: %s: EMPTY REQUEST!!!" 
                                 % self)
            self.log.warning('TRADERMANAGER: %s: Request received: "%s"' % (self, data))
            try:
                trade_request = TradeRequest(data.split('-')[:4]) 
            except IndexError, e:
                self.log.warning("TRADERMANAGER: %s: Malformed Request."
                                 "Ignored!" % self)
            res = self.trader_manager.pass_request_to_se(trade_request)
            self.log.warning('TRADERMANAGER: %s Send response: "%s"'
                             % (self, res))
            self.conn.send(res)

        def stop(self):
            self.log.warning('TRADERMANAGER: %s: We are stopping!' % self)
            self.stopped = True

    def __init__(self, se=None):
        object.__init__(self)
        self.log = logging.getLogger("TraderManager")

        self.name = 'TraderManager'
        self.stock_exchange = se

        self.server_thread = TraderManager.ServerThread(self)
        self.server_thread.daemon = True
        self.server_thread.start()

    def __str__(self):
        return self.name

    def init_db(self):
        try:
            self.db = sqlite3.connect('traders.db')   # create if db file no exist
        except sqlite3.DatabaseError, e:
            self.log.warning("TRADERMANAGER: %s: Connect to database: %s"
                             % (self, e))
            return False
        except:
            self.log.warning("TRADERMANAGER: %s: SQLITE: Uncaught error" % self)
            return False

        self.db.isolation_level = None
        self.cursor = self.db.cursor()

        # The problem is how to check date integrity of a database???
        # Check if db has been initialized
        try:
            self.cursor.execute("SELECT COUNT(*) FROM dbstatus")     
        except sqlite3.OperationalError, e:     # Table doesn't exist
            # db not initialized
            self.log.warning("TRADERMANAGER: %s: SQLITE: %s" % (self, e))

            self.cursor.executescript(CREATE_DB_STATUS_SCRIPT)
            self.cursor.execute("INSERT INTO dbstatus VALUES (1, %s)" 
                                % (str(datetime.datetime.now())[:-7]))
            self.cursor.executescript(CRTEAT_TRADER_INFO_TABLE)
            self.cursor.executescript(CRTEAT_TRADER_REQUEST_TABLE_SCRIPT)
            self.cursor.executescript(INSERT_DEFAULT_TARDERS_SCRIPT)
        except:
            self.log.warning("TRADERMANAGER: %s: SQLITE: Uncaught error" % self)
        finally:
            # Update db access time
            self.cursor.execute("UPDATE dbstatus SET modification='%s'" 
                                % (str(datetime.datetime.now())[:-7]))

        self.db.commit()
        #self.db.close()

        return True

    def pass_request_to_se(self, req):
        self.stock_exchange.accept_request(req)

        return TRADE_STATE_A

    def is_client_valid(self, c):
        return False

    def loggin(self):
        pass

    #@onexit
    def stop(self):
        self.log.warning('TRADERMANAGER: %s: We are stopping!' % self)
        self.server_thread.stop()


